// First, we declare this grammar as just a lexer and give it a name
grammar MinC;

// NOTE: The @header and @members only applies to the parser. There is a
// separate section for the lexer, below.
@header {
    // This section is pasted into the top of the generated
    // file.  We use it to declare a package here.  Other packages
    // can also be imported as required.
    package cs536.syntax;

    import static cs536.ast.AbstractSyntaxTree.*;
    import cs536.ast.SourceLocation;
}

@members {
    // Utility function for turning a token into a source location. (These
    // will be used in future projects.) For now don't worry too much about
    // where you set your source locations; if you want to keep using your
    // code, make it "reasonable." In particular, for declarations, make it
    // the same as the thing being declared.
    private SourceLocation loc(Token tok) {
        return new SourceLocation(tok);
    }


    // Some utility functions for creating a list of one element.
    private List<Expression> makeList(Expression arg) {
        List<Expression> l = new ArrayList<Expression>();
        l.add(arg);
        return l;
    }

    private List<Statement> makeList(Statement arg) {
        List<Statement> l = new ArrayList<Statement>();
        l.add(arg);
        return l;
    }


    private List<FormalArg> makeList(FormalArg arg) {
        List<FormalArg> l = new ArrayList<FormalArg>();
        l.add(arg);
        return l;
    }


    // This overrides the function in the parser class BaseRecognizer
    // (provided by Antlr). It's called by Antlr to print the syntax
    // errors. We will hijack it so that we know there was a problem.
    public void emitErrorMessage(String msg)
    {
        super.emitErrorMessage(msg);
        encounteredError = true;
    }

    private boolean encounteredError = false;

    public boolean isSuccessful() {
        return !encounteredError;
    }
}


@lexer::header {
    // This section is pasted into the top of the generated
    // file.  We use it to declare a package here.  Other packages
    // can also be imported as required.
    package cs536.syntax;
}

@lexer::members {
    // This overrides the function in the parser class BaseRecognizer
    // (provided by Antlr). It's called by Antlr to print the syntax
    // errors. We will hijack it so that we know there was a problem.
    public void emitErrorMessage(String msg)
    {
        super.emitErrorMessage(msg);
        encounteredError = true;
    }

    private boolean encounteredError = false;

    public boolean isSuccessful() {
        return !encounteredError;
    }



    // Emits a token with a string representation of 'str'
    private void emitRepresentation(int type, String str) {
        emit(new MinCToken(str, type, input, state, getCharIndex()-1));
    }

    // Emits a token with a string representation of '{prefix}{lexeme}' where
    // prefix is specified and lexeme is the current lexeme
    private void emitPrefixed(int type, String prefix) {
        emitRepresentation(type, prefix + getCurrentLexeme());
    }

    // Emit an integer literal
    private void emitIntLit(long val) {
        emit(new IntLitToken(val, INT_LIT, input, state, getCharIndex()-1));
    }

    // Emit a floating point literal
    private void emitFloatLit(double val) {
        emit(new FloatLitToken(val, FLOAT_LIT, input, state, getCharIndex()-1));
    }


    // Returns the lexeme that is currently being matched
    private String getCurrentLexeme() {
        return input.substring(state.tokenStartCharIndex, getCharIndex() - 1);
        //MinCToken t = new MinCToken(input, state, getCharIndex()-1);
        //return t.getText();
    }


    // Emits a token for an illegal escape sequence. This one is a little obnoxious.
    private void emitIllegalEscape() {
        // Remove any escaped \s so that we can just look at the remaining
        // ones.  That is, remove \\ from the string. The regex for that is
        // \\\\ since \ is special, which means we need to type \\\\\\\\ for
        // the actual string.
        String s = getCurrentLexeme().replaceAll("\\\\\\\\", "");
        int pos = -1;
        while ((pos = s.indexOf('\\', pos+1)) != -1) {
            char c = s.charAt(pos+1);
            if (c != 'n' && c != 't' && c != '\"' && c != '\\') {
                emitRepresentation(STRING_WITH_ILLEGAL_ESCAPE, "Illegal escape sequence: \\" + c);
                return;
            }
        }
        assert false;
    }
}

//////////////////////////////////////////////////////////////////////////

////////   Parser

//////////////////////////////////////////////////////////////////////////


// What is this doing? It's returning a list of declarations. How do we form
// that list? Basically, the first action runs right at the beginning, before
// any declarations are matched. Then, each time a declaration is matched
// (with the use of 'd=decl'), it runs the second action. What we add to the
// list is the translation of 'decl', which is stored in the variable 'd'.
//
// Note that the grammar rule without any business from actions is
//    program : decl* ;
//
// You'll have a number of places where you'll "need" to create a list like
// this. If the list can't be empty, perhaps you use one of the 'makeList'
// functions above to start things off instead of the empty list.
//
// Yes, we're mutating state here... it's a little inelegant perhaps, but it
// suffices.
program returns [List<GlobalDeclaration> list]
    :             { list = new ArrayList<GlobalDeclaration>(); }
        (d=decl   { list.add(d); }
        )*
    ;   

    

groundType returns [TypeNode node]
    : l=INT      { node = new IntType(loc(l));     }
    | d=DOUBLE   { node = new DoubleType(loc(d));   }
    | j=STRING   { node = new StringType(loc(j)); }
    | b=BOOL     { node = new BooleanType(loc(b)); }
    ;

type returns [TypeNode node]
    : t=groundType                                  { node = t; }
    | l=groundType j=STAR                        { node = new PointerType(loc(j), l);}
    | m=groundType LBRACKET n=INT_LIT RBRACKET    { node = new ArrayType(loc(n), m, ((IntLitToken)n).getValue());}
    ;
   

funcType returns [TypeNode node]
    : ty=type  { node = ty; }
    | VOID     { node = null; }
    | /* epsilon */ { node = null; }
    ;
    
formalList  returns [List<FormalArg> list] 
    : n=nonEmptyFormalList      { list=n; }
    | /* epsilon */             { list = new ArrayList<FormalArg>(); }
    ;

nonEmptyFormalList returns [List<FormalArg> list] 
    : { list = new ArrayList<FormalArg>(); }
    f=formal          { list.add(f); }
    (t=formalTail      { list.add(t); })*
    ;

formalTail  returns [FormalArg node]
    : COMMA f=formal                  { node = f; }
    ;

formal  returns [FormalArg node]
    : t=type name=IDENT                 { node = new FormalArg(loc(name), t, name.getText()); }
    ;
    


// I'll show you how to deal with the dangling else problem. Since there's no
// way to write an unambiguous grammar for it.
stmt returns [Statement node]
    : e=exp SEMI               { node = new ExpressionStmt(e.getLocation(), e); }                    
    
    //var decl
    | v=varDecl                { node = new VarDeclStmt(v.getLocation(), v.getType(),
                                v.getName(), v.getInitializer()); }
    
    //return statement
    | r=RETURN SEMI             { node = new ReturnStmt(loc(r), null); }
    
    | z=RETURN e=exp SEMI       { node = new ReturnStmt(loc(z), e); } 
    
    
    //if statement
    | i=IF LPAREN cond=exp RPAREN thenBody=stmtOrBody
        (options {greedy=true;} 
         : ELSE elseBody=stmtOrBody  { node = new IfStmt(loc(i), cond, thenBody, elseBody); }
        |                            { node = new IfStmt(loc(i), cond, thenBody, new ArrayList<Statement>()); }
        )
        
    //while statement  
    | w=WHILE LPAREN cond=exp RPAREN body=stmtOrBody  { node = new WhileStmt(loc(w), cond, body); }   
     
    //write statement
    | x=WRITE cond=exp SEMI  { node = new WriteStmt(loc(x), cond); }
    
    //read statement
    | y=READ dest=atomExp SEMI  { node = new ReadStmt(loc(y), dest.toString()); } 
    

    ;

stmtOrBody returns [List<Statement> list]
    : s=stmt                      { list = makeList(s); }
    | LBRACE t=stmtList RBRACE    { list = t; }
    ;
    
stmtList  returns [List<Statement> list] 
    :             { list = new ArrayList<Statement>(); }
        (d=stmt   { list.add(d); }
        )*
    ;  


// Note that this rule isn't quite right...
funcDecl returns [FuncDecl node]
    : ret=funcType name=IDENT LPAREN list=formalList RPAREN LBRACE body=stmtList RBRACE
        { node = new FuncDecl(loc(name), ret, name.getText(), list, body); }
     
    ;
    
varDecl returns [GlobalVarDecl node]
    : t=type name=IDENT SEMI
      { node = new GlobalVarDecl(loc(name), t, name.getText(), null);}
      
    | ty=type id=IDENT ASSIGN e=exp SEMI
      { node = new GlobalVarDecl(loc(id), ty, id.getText(), e);}
    ;


decl returns [GlobalDeclaration node]
    : f=funcDecl         { node = f; }
    | v=varDecl          { node = v; }
    ;


literalExp returns [LiteralExp node]
    : l=INT_LIT            { node = new IntLit(loc(l),    ((IntLitToken)l).getValue());   }
    | f=FLOAT_LIT          { node = new DoubleLit(loc(f), ((FloatLitToken)f).getValue());  }
    | t=TRUE               { node = new BooleanLit(loc(t), true); }
    | fa=FALSE             { node = new BooleanLit(loc(fa), false); }
    | s=STRING_LIT         { node = new StringLit(loc(s), s.toString()); }    
    //| b=IDENT              { node = new VarRefExp(loc(b), b.getText()); }
    | k=NULL                 { node = literalExp(); }
    ;
    
    
actualsList returns [ List<Expression> list ] 
  : n=nonEmptyActualsList      { list = n; }
  | /* epsilon */              { list = new ArrayList<Expression>(); }
  ; 

nonEmptyActualsList returns [ List<Expression> list ] 
  : { list = new ArrayList<Expression>(); }
    e=exp                 { list.add(e); }        
    (a=actualsTail         { list.add(a); })*
  ;

actualsTail returns [ Expression node ]
  : COMMA e=exp           { node = e; }
  ;
  
  

//    Order of precedence:
// Array access and function calls
// Postfix increment and decrement
// Prefix increment and decrement
// Unary minus, logical not
// Pointer dereference (++STAR exp++)
// Address-of
// Multiplication, division, modulus
// Addition and subtraction
// Relational less/greater (not associative)
// Relational (not) equal to
// Logical AND
// Logical OR
// Assignment (right associative)

// I have filled in a few parts for you. However, I've skipped precedence
// levels and have sometimes skipped choices within a level, so you'll have
// to modify my rules in addition to adding your own. (For instance, I go
// right from the plus/minus level to unary minus; that's a jump of 4
// levels. So things compile, I have the plus/minus productions use the
// nonterminal corresponding to the unary minus level.)
//
// (There are also some incomplete rules above.)


atomExp returns [Expression node]
    : lit=literalExp             { node = lit; }
    ;   

    
// Need separate expression rules for array accesses and function calls


// Unary operators cannot actually be associative, but operators that appear
// on the left (e.g. -5) you'll want to treat as right associative and use
// right recursion. Even though there's no associativity, you can still think
// of building up the expression from the right: the final operand (e.g. the
// 5 in -5) will be at the bottom of the tree. Similarly, if there is a unary
// postfix operator, you want to use either left recursion or a *.
minusExp returns [Expression node]
    : m=MINUS e=atomExp    { node = new NegationOp(loc(m), e); }
    | n=LNOT e=atomExp     { node = new NotOp(loc(n),e);}
    | e=atomExp             { node = e; }
    ;
    
derefExp returns [Expression node]   
    : s=STAR e=minusExp              { node = new DerefOp(loc(s),e); }
    | e=minusExp                   {node = e;}
    ;

    
addressExp returns [Expression node]
    : a=ADDROF e=addressExp          { node = new AddrOfOp(loc(a), e); }
    | e=derefExp                     { node = e; }
    ;


// Here's how a left-associative operator looks. For left associative
// operators, the first operand (e.g. 1 in 1+2+3) should appear at the bottom
// of the tree, so you can think of stacking new nodes on top of the tree the
// further right in the expression you look. That's what this is doing. It
// starts off with 'node' being the first operand, then successively creates
// new parent nodes as it sees more + and - signs.
//
// Note that we write it 'minusExp (PLUS multExp | MINUS minusExp)' instead
// of 'minusExp ((PLUS|MINUS) minusExp)' because I want to create different
// AST nodes in each case.

multExp returns [Expression node]
    : e=addressExp                  { node = e; }
        ( s=STAR r=addressExp       { node = new MultiplyOp(node.getLocation(), node, r); }
        | d=DIV r=addressExp        { node = new DivideOp(node.getLocation(), node, r); }
        | m=MOD r=addressExp        { node = new ModOp(node.getLocation(), node, r); }        
        )*
    ;

addExp returns [Expression node]
    : e=multExp                 { node = e; }
        ( p=PLUS r=multExp      { node = new AddOp(node.getLocation(), node, r); }
        | m=MINUS r=multExp     { node = new SubtractOp(node.getLocation(), node, r); }
        )*
    ;
    
comparisonExp returns [Expression node]
    : e=addExp                 { node=e; }
       ( ( s=LT                 { node = new LessThanOp(node.getLocation(), node, r); }
        | s=LTE                 { node = new LessOrEqualOp(node.getLocation(), node, r); }
        | s=GT                  { node = new GreaterThanOp(node.getLocation(), node, r); }
        | s=GTE                 { node = new GreaterOrEqualOp(node.getLocation(), node, r); }
        )
      r=addExp )?
    ;
    
equalsExp returns [Expression node]
    : e=comparisonExp                 { node=e; }
        ( s=EQUAL r=comparisonExp     { node = new EqualsOp(node.getLocation(), node, r); }
        | s=NEQUAL r=comparisonExp    { node = new NotEqualsOp(node.getLocation(), node, r); } 
      )*      
    ;
    
andExp returns [Expression node]
    : e=equalsExp                { node=e; }
        ( s=LAND r=equalsExp      { node = new AndOp(node.getLocation(), node, r); }
      )*      
    ;
    
orExp returns [Expression node]
    : e=andExp                { node=e; }
        ( s=LOR r=andExp      { node = new OrOp(node.getLocation(), node, r); }
      )*      
    ;       
    


// I'm not going to give you this rule, but I'll give a couple hints because
// it's a little different from what we've seen. Assignment is
// right-associative. Even though I have *, I still want to write a
// right-recursive rule:
//
//    assignExp -> addExp assignExp | addExp
//
// However, those two productions have a common prefix. (And it's a
// complicated prefix that can have parentheses in it.) Thus you need to left
// factor, either as we will talk about in class and the notes cover, or you
// can use Antlr's EBNF support.
//
// Note that Antlr would tell you this if you tried to do the above:
//    rule addExp has non-LL(*) decision due to recursive
//    rule invocations reachable from alts 2,3.  Resolve
//    by left-factoring or using syntactic predicates or
//    using backtrack=true option.
// and know that setting backtrack=true is not a good idea.

assignExp returns [Expression node]
    : e=orExp               
      //(a=assignExp | /* epsilon */ ) { node = a; }
      //a=assignExp2   { node=a; }  
      
      ( ASSIGN a=assignExp    { node = new AssignOp(node.getLocation(), e, a); }
      | /* epsilon */ )       { node = e; }
        
    ;
    
//assignExp2 returns [Expression node]
//    :  ASSIGN a=assignExp     { node = new AssignOp(node.getLocation(), null, a); }
//    | /* epsilon */           { node = null; }
//    ;


exp returns [Expression node]
    : e=assignExp   { node = e; }                                
  ;
    

  

  




//////////////////////////////////////////////////////////////////////////

////////   Lexer

//////////////////////////////////////////////////////////////////////////

fragment LOWER: 'a'..'z';
fragment UPPER: 'A'..'Z';
fragment ALPHA: UPPER | LOWER;
fragment DIGIT: '0'..'9';




WHITESPACE
    : (' ' | '\r'? '\n' | '\t') { skip(); }
    ;

LINE_COMMENT
    : ('//' ~'\n'*)   {skip();}
    ;

fragment BLOCK_COMMENT_CONTENTS : '*'+ ~('*' | '/') | ~'*';
BLOCK_COMMENT
    : ('/*' BLOCK_COMMENT_CONTENTS* '*'* '*/')  {skip();}
    ;

UNTERM_BLOCK_COMMENT
    : ('/*' BLOCK_COMMENT_CONTENTS* '*'*)  { emitRepresentation(UNTERM_BLOCK_COMMENT, "Unterminated block comment"); }
    ;



/////////////////////////////////////
// Keywords and operators

BOOL:   'bool'     { emitRepresentation(BOOL, "BOOL"); };
DOUBLE: 'double'   { emitRepresentation(DOUBLE, "DOUBLE"); };
ELSE:   'else'     { emitRepresentation(ELSE, "ELSE"); };
FALSE:  'false'    { emitRepresentation(FALSE, "FALSE"); };
IF:     'if'       { emitRepresentation(IF, "IF"); };
INT:    'int'      { emitRepresentation(INT, "INT"); };
NULL:   'null'     { emitRepresentation(NULL, "NULL"); };
READ:   'read'     { emitRepresentation(READ, "READ"); };
RETURN: 'return'   { emitRepresentation(RETURN, "RETURN"); };
STRING: 'string'   { emitRepresentation(STRING, "STRING"); };
TRUE:   'true'     { emitRepresentation(TRUE, "TRUE"); };
VOID:   'void'     { emitRepresentation(VOID, "VOID"); };
WHILE:  'while'    { emitRepresentation(WHILE, "WHILE"); };
WRITE:  'write'    { emitRepresentation(WRITE, "WRITE"); };

LPAREN:   '('      { emitRepresentation(LPAREN, "LPAREN"); };
RPAREN:   ')'      { emitRepresentation(RPAREN, "RPAREN"); };
LBRACE:   '{'      { emitRepresentation(LBRACE, "LBRACE"); };
RBRACE:   '}'      { emitRepresentation(RBRACE, "RBRACE"); };
LBRACKET: '['      { emitRepresentation(LBRACKET, "LBRACKET"); };
RBRACKET: ']'      { emitRepresentation(RBRACKET, "RBRACKET"); };
SEMI:     ';'      { emitRepresentation(SEMI, "SEMI"); };
COMMA:    ','      { emitRepresentation(COMMA, "COMMA"); };
LAND:     '&&'     { emitRepresentation(LAND, "LAND"); };
LOR:      '||'     { emitRepresentation(LOR, "LOR"); };
LNOT:     '!'      { emitRepresentation(LNOT, "LNOT"); };
ASSIGN:   '='      { emitRepresentation(ASSIGN, "ASSIGN"); };
ADDROF:   '&'      { emitRepresentation(ADDROF, "ADDROF"); };
LT:       '<'      { emitRepresentation(LT, "LT"); };
LTE:      '<='     { emitRepresentation(LTE, "LTE"); };
GT:       '>'      { emitRepresentation(GT, "GT"); };
GTE:      '>='     { emitRepresentation(GTE, "GTE"); };
EQUAL:    '=='     { emitRepresentation(EQUAL, "EQUAL"); };
NEQUAL:   '!='     { emitRepresentation(NEQUAL, "NEQUAL"); };
INCR:     '++'     { emitRepresentation(INCR, "INCR"); };
DECR:     '--'     { emitRepresentation(DECR, "DECR"); };
PLUS:     '+'      { emitRepresentation(PLUS, "PLUS"); };
MINUS:    '-'      { emitRepresentation(MINUS, "MINUS"); };
STAR:     '*'      { emitRepresentation(STAR, "STAR"); };
DIV:      '/'      { emitRepresentation(DIV, "DIV"); };
MOD:      '%'      { emitRepresentation(MOD, "MOD"); };


/////////////////////////////////
// Identifiers

fragment IDENT_START:    ALPHA | ('_' | '$')+ (ALPHA | DIGIT);
fragment IDENT_CONTINUE: (ALPHA | DIGIT | '_' | '$')*;

IDENT
    : IDENT_START IDENT_CONTINUE  { emitPrefixed(IDENT, "IDENT: "); }
    ;


// "If a lexeme that would otherwise be an identifer contains only dollar
// signs and underscores or begins with a numeric character..."
BAD_IDENT
    : (
        ('_' | '$')+ 
      | (DIGIT+ (ALPHA | '_' | '$') IDENT_CONTINUE)
      )
    { emitPrefixed(BAD_IDENT, "Invalid identifier: "); };


////////////////////////////////////
// Strings

fragment VALID_ESCAPE_CHAR: '\\' | 'n' | 't' | '"';

// Anything is good in a string except for \ and ".
// 32: space
// 33: !
// 34: "
// 35: #
// 133: [
// 134: \
// 135: ]
// 126: ~
fragment STRING_CHAR: ' ' | '!' | '#'..'[' | ']'..'~' | '\\' VALID_ESCAPE_CHAR;


STRING_LIT
    : '"' STRING_CHAR* '"'  { emitPrefixed(STRING_LIT, "STRING_LIT: "); }
    ;

// Illegal escapes
fragment ILLEGAL_ESCAPE: '\\' ~(VALID_ESCAPE_CHAR);
fragment ILLEGAL_ESCAPE_STRING_CHAR: STRING_CHAR | ILLEGAL_ESCAPE;

STRING_WITH_ILLEGAL_ESCAPE
    : '"' STRING_CHAR*  ILLEGAL_ESCAPE  ILLEGAL_ESCAPE_STRING_CHAR* '"'
      { emitIllegalEscape(); }
    ;

// Strings with tabs can have illegal escapes
fragment TAB_STRING_CHAR: ILLEGAL_ESCAPE_STRING_CHAR | '\t';

STRING_WITH_TAB
    : '"' ILLEGAL_ESCAPE_STRING_CHAR* '\t' TAB_STRING_CHAR* '"'
      { emitRepresentation(STRING_WITH_TAB, "Illegal string character"); }
    ;

// Unterminated strings can have both illegal escape sequences and tabs
UNTERM_STRING
    : '"' TAB_STRING_CHAR* 
      { emitPrefixed(UNTERM_STRING, "Unterminated string: "); }
    ;



/////////////////////////////
// Integers

fragment INTEGER: ('0' | '1'..'9' DIGIT*);

INT_LIT
    : INTEGER
        {
            try {
                long val = Long.parseLong(getCurrentLexeme());
                long max = - (long) Integer.MIN_VALUE;
                if (val > max) {
                    emitPrefixed(INT_LIT, "Integer out of range: ");
                }
                else {
                    emitIntLit(val);
                }
            }
            catch (NumberFormatException n) {
                emitPrefixed(INT_LIT, "Integer out of range: ");
            }
        }
    ;

LEADING_ZERO_INT
    : '0' DIGIT+  { emitPrefixed(LEADING_ZERO_INT, "Integer has leading 0: "); }
    ;


/////////////////////////////
// Floats

FLOAT_LIT
    : INTEGER '.' DIGIT+ ('e' '-'? INTEGER)?
      {
          try {
              double val = Double.parseDouble(getCurrentLexeme());
              if (Double.isInfinite(val)) {
                  emitPrefixed(FLOAT_LIT, "Floating point out of range: ");
              }
              else {
                  emitFloatLit(val);
              }
          }
          catch (NumberFormatException n) {
              emitPrefixed(FLOAT_LIT, "Floating point out of range: ");
          }
      }
    ;

LEADING_ZERO_FLOAT
    : ( '0' DIGIT+ '.' DIGIT+ ('e' '-'? INTEGER)?
      | INTEGER    '.' DIGIT+ 'e' '-'? '0' DIGIT+
      | '0' DIGIT+ '.' DIGIT+ 'e' '-'? '0' DIGIT+
      )
      { emitPrefixed(FLOAT_LIT, "Floating point has leading 0: "); }
    ;
